package com.jiangyg.mall.db.hibernate.mapper;

import com.jiangyg.mall.core.exception.SqlMapperException;
import com.jiangyg.mall.core.support.cache.CacheAdapter;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;

import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.regex.Pattern;

/**
 * 类描述：SQL的Xml文件解析
 * 
 * @author jiangyg
 * @date 2019-10-19
 * @version 2.0
 */
public class SQLXmlResolver {

    /**
     * 日志
     */
    private static final Logger logger = LoggerFactory.getLogger(SQLXmlResolver.class);

    /**
     * 缓存适配器
     */
    private final CacheAdapter cacheAdapter;

    /**
     * sql-query映射文件路径集合
     */
    private final List<String> mappingLocations;

    /**
     * 命令查询正则
     */
    private final Pattern nameQueryPattern = Pattern.compile("[a-zA-Z0-9\\.]+");

    public SQLXmlResolver(final CacheAdapter cacheAdapter, final List<String> mappingLocations) {
        this.cacheAdapter = cacheAdapter;
        this.mappingLocations = mappingLocations;
    }

    /**
     * 功能描述：XML配置文件解析
     * 
     */
    public void execute() throws Exception {
        if (logger.isDebugEnabled()) {
            logger.debug("开始初始化SQL集合……");
        }
        PathMatchingResourcePatternResolver pmrpResolver = new PathMatchingResourcePatternResolver();
        // 解析xml
        SAXReader reader = new SAXReader();
        reader.setValidation(false);
        reader.setEntityResolver(new IgnoreEntityResolver());
        for (String mappingLocation : mappingLocations) {
            Resource[] resources = pmrpResolver.getResources(mappingLocation);
            if (resources == null || resources.length == 0) {
                return;
            }
            // 解析所有的配置文件
            for (Resource resource : resources) {
                InputStreamReader is = null;
                // 配置文件名称
                String fileName = resource.getFilename();
                try {
                    if (logger.isDebugEnabled()) {
                        logger.debug("开始解析动态SQL配置文件[" + fileName + "]……");
                    }
                    is = new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8);
                    Document doc = reader.read(is, "UTF-8");
                    // 解析sql-insert处理
                    dynamicSQLResolver(doc, "sql-insert");
                    // 解析sql-delete处理
                    dynamicSQLResolver(doc, "sql-delete");
                    // 解析sql-update处理
                    dynamicSQLResolver(doc, "sql-update");
                    // 解析sql-query处理
                    dynamicSQLResolver(doc, "sql-query");
                } catch (Exception ex) {
                    logger.error("解析动态SQL配置文件[" + fileName + "]出错：", ex);
                    throw ex;
                } finally {
                    if (is != null) {
                        is.close();
                    }
                }
            }
        }
        if (logger.isDebugEnabled()) {
            logger.debug("初始化SQL集合完成……");
        }
    }

    /**
     * 功能描述：动态SQL解析
     * 
     * @param doc      上下文
     * @param sqlType  SQL类型
     */
	private void dynamicSQLResolver(final Document doc, final String sqlType) {
        List<Element> elements = doc.getRootElement().elements(sqlType);
        for (Element element : elements) {
            String name = attributeValue(element, "name", true);
            String value = element.getStringValue();
            String result = this.check(name, value);
            if (StringUtils.isNotBlank(result)) {
                throw new SqlMapperException(result);
            }
            this.cacheAdapter.put(name, new SQLObject(name, value));
        }
    }

    /**
     * 功能描述：获取元素属性值
     * 
     * @param element 元素
     * @param name    属性
     * @param require 属性是否必须存在
     * @return 元素属性值
     */
    private String attributeValue(final Element element, String name, boolean require) {
        Attribute attr = element.attribute(name);
        if (require && attr == null) {
            throw new SqlMapperException("元素[" + element.getName() + "]缺少属性[" + name + "],请检查！");
        }
        if (attr == null) {
            return null;
        }
        return attr.getStringValue();
    }

    /**
     * 功能描述：检查sql是否合理
     * 
     * @param name  sql的命名
     * @param value sql字符串
     */
    private String check(final String name, final String value) {
        if (StringUtils.isBlank(name)) {
            return "SQL命名不能为空！";
        }
        if (this.cacheAdapter.containsKey(name)) {
            return "SQL出现重复的sql-query[" + name + "]！";
        }
        if (!nameQueryPattern.matcher(name).matches()) {
            return "SQL的命名规则只能包含字符、数字和.，命名[" + name + "]违反规则！";
        }
        return null;
    }

    /**
     * 类描述：忽略自定义DTD声明实体
     * 
     * @author jiangyg
     * @date 2019-10-13
     * @version 2.0
     */
    private static class IgnoreEntityResolver implements EntityResolver {

        @Override
        public InputSource resolveEntity(String publicId, String systemId) {
            return new InputSource(new ByteArrayInputStream("<?xml version='1.0' encoding='UTF-8'?>".getBytes()));
        }

    }

}
